#pragma once

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/wait.h>
#include <pthread.h>

#include "log.hpp"
#include "Inetaddr.hpp"
#include "threadpool.hpp"

using namespace log_namespace;

enum
{
    SOCKET_ERROR = 1
    ,BIND_ERROR
    ,LISTEN_ERROR
};

const static int gport = 8888;
const static int gsock = -1;
const static int gbacklog = 8;

using task_t = std::function<void()>;

using command_service_t = std::function<void(int socketfd, inetAddr addr)>;

class tcpServer
{    
public:
    tcpServer(command_service_t service, uint16_t port = gport)
        :_port(gport)
        ,_listensocketfd(gsock)
        ,_isrunning(false)
        ,_service(service)
    {
        
    }

    void initServer()
    {
        _listensocketfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocketfd < 0)
        {
            LOG(FATAL, "socket create error\n");
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success, socketfd: %d\n", _listensocketfd);

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        // 绑定本主机的套接字和 对方主机的地址和端口号
        if(::bind(_listensocketfd, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            LOG(FATAL, "bind error!\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        // tcp是面向连接的，tcp需要未来不断地获取链接
        if (::listen(_listensocketfd, gbacklog) < 0)
        {
            LOG(FATAL, "bind error\n");
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen success\n");

    }

    // version 2: 多线程版本
    class threadData
    {
    public:
        threadData(int socketfd, tcpServer *tcS, const inetAddr &addr)
            :_ssocketfd(socketfd)
            ,_self(tcS)
            ,_addr(addr)
        {

        }
        int _ssocketfd;
        tcpServer *_self;
        inetAddr _addr;
    };

    static void *execute(void* args)
    {
        pthread_detach(pthread_self());
        threadData *td = static_cast<threadData *>(args);
        td->_self->_service(td->_ssocketfd, td->_addr);
        ::close(td->_ssocketfd);

        delete td;
        return nullptr;
    }
    
    // 子线程和主线程 共享一个 文件指针数组，所以不能通过随意关闭任意一个文件描述符
    void loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // 获取新连接
            // accept 这个函数利用 _socketfd建立好的链接，
            // 创建一个能够处理任务的件，并返回这个文件的文件描述符
            int socketfd = accept(_listensocketfd, (struct sockaddr*)&client, &len);
            if (socketfd < 0)
            {
                LOG(WARNING, "accept error\n");
                continue;
            }
            inetAddr addr(client);
            LOG(INFO, "get a new link, client info: %s\n, socketfd is: %d\n", addr.addrStr().c_str(), socketfd);

            pthread_t tid;
            threadData *td = new threadData(socketfd, this, addr);
            pthread_create(&tid, nullptr, execute, td);
        }
        _isrunning = false;
    }

    // void service(int socketfd, inetAddr addr)
    // {
    //     // 如果按照如下设计 服务，那么一旦客户端连上了服务器
    //     // 只要客户端没有退出，服务器端就会一直为这个客户端提供服务
    //     // 这种服务称为长服务
        // while (true)
        // {
        //     char inbuffer[1024];
        //     ssize_t n = ::read(socketfd, inbuffer, sizeof(inbuffer) - 1);
        //     if (n > 0)
        //     {
        //         inbuffer[n] = 0;
        //         LOG(INFO, "get message from client %s, message: %s\n", addr.addrStr().c_str(), inbuffer);
        //         std::string echo_string = "[server echo]# ";
        //         echo_string += inbuffer;
        //         write(socketfd, echo_string.c_str(), echo_string.size());
        //     }
        //     else if(n == 0)
        //     {
        //         LOG(INFO, "client %s quit\n", addr.addrStr().c_str());
        //         break;
        //     }
        //     else
        //     {
        //         LOG(ERROR, "read error: %s\n", addr.addrStr().c_str());
        //         break;
        //     }
        // }
        // ::close(socketfd);
    // }



    ~tcpServer()
    {

    }
private:
    uint16_t _port;
    int _listensocketfd;
    bool _isrunning;
    command_service_t _service;
};


